package edu.uiuc.ncsa.rdftokml;

import edu.uiuc.ncsa.commons.NCSAKMLUtilityException;
import edu.uiuc.ncsa.commons.util.Util;
import edu.uiuc.ncsa.commons.util.XSLUtil;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author : Sachith Dhanushka (sachith.dhanushka@gmail.com)
 * @date : : May 26, 2008 - 3:03:49 PM
 */
public class Converter {

    private static final String RDF_TO_KML_XSL_FILE = "resources/rdf_to_kml.xsl";

    public void convert(InputStream input, OutputStream output) throws NCSAKMLUtilityException {
//        XSLUtil.xslTransform(input, output, RDF_TO_KML_XSL_FILE);


        try {
            Document sourceDocument = Util.parseXmlFile(input);
            Element sourceDocElement = sourceDocument.getDocumentElement();

            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document outputDocument = builder.newDocument();

            // define root level namespaces
            Element outputDocumentElement = outputDocument.createElement("kml");
            outputDocument.appendChild(outputDocumentElement);
            outputDocumentElement.setAttribute("xmlns", "http://earth.google.com/kml/2.2");

            // add folder
            Element folderElement = outputDocument.createElement("Folder");
            outputDocumentElement.appendChild(folderElement);

            NodeList dataStreamTokensList = evaluateXPath(sourceDocElement, "/RDF/DataStreamToken/GroundOverlay");
            if (dataStreamTokensList != null) {
                for (int index = 0; index < dataStreamTokensList.getLength(); index++) {
                    Element dsTokenElement = (Element) dataStreamTokensList.item(index);

                    // get start time
                    String startTime = "";
                    NodeList startTimeList = evaluateXPath(dsTokenElement, "TimeSpan/Interval/hasBeginning/Instant/inXSDDateTime");
                    if (startTimeList != null) startTime = ((Element) startTimeList.item(0)).getTextContent();

                    // get end time
                    String endTime = "";
                    NodeList endTimeList = evaluateXPath(dsTokenElement, "TimeSpan/Interval/hasEnd/Instant/inXSDDateTime");
                    if (endTimeList != null) endTime = ((Element) endTimeList.item(0)).getTextContent();

                    // get north, south, east west cordinates
                    String cordinates = "";
                    NodeList cordinatesList = evaluateXPath(dsTokenElement, "spatial/Box/coordinates");
                    if (cordinatesList != null) cordinates = ((Element) cordinatesList.item(0)).getTextContent();

                    String[] cordinatesArray = cordinates.split(",");

                    Element groundOverlayElement = outputDocument.createElement("GroundOverlay");
                    folderElement.appendChild(groundOverlayElement);

                    // add Name element
                    Element nameElement = outputDocument.createElement("Name");
                    groundOverlayElement.appendChild(nameElement);
                    nameElement.setTextContent("Box #" + index);

                    // add time span
                    Element timeSpanElement = outputDocument.createElement("TimeSpan");
                    groundOverlayElement.appendChild(timeSpanElement);

                    //    add begin element
                    Element beginElement = outputDocument.createElement("begin");
                    timeSpanElement.appendChild(beginElement);
                    beginElement.setTextContent(startTime);

                    //    add end element
                    Element endElement = outputDocument.createElement("end");
                    timeSpanElement.appendChild(endElement);
                    endElement.setTextContent(endTime);

                    // add LatLon box
                    Element latLonBoxElement = outputDocument.createElement("LatLonBox");
                    groundOverlayElement.appendChild(latLonBoxElement);

                    // add cordinates
                    Element north = outputDocument.createElement("north");
                    latLonBoxElement.appendChild(north);
                    north.setTextContent(cordinatesArray[0]);

                    Element west = outputDocument.createElement("west");
                    latLonBoxElement.appendChild(west);
                    west.setTextContent(cordinatesArray[1]);

                    Element south = outputDocument.createElement("south");
                    latLonBoxElement.appendChild(south);
                    south.setTextContent(cordinatesArray[2]);

                    Element east = outputDocument.createElement("east");
                    latLonBoxElement.appendChild(east);
                    east.setTextContent(cordinatesArray[3]);

                }

                serialize(outputDocument, output);
            }

        } catch (ParserConfigurationException e) {
            throw new NCSAKMLUtilityException(e);
        } catch (XPathExpressionException e) {
            throw new NCSAKMLUtilityException(e);
        }

    }

    public void convert(String inputFile, String outputFile) throws NCSAKMLUtilityException {
        XSLUtil.xslTransform(inputFile, outputFile, RDF_TO_KML_XSL_FILE);
    }

    public NodeList evaluateXPath(Node node, String xpathString) throws XPathExpressionException {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XPathExpression expr = xpath.compile(xpathString);
        return (NodeList) expr.evaluate(node, XPathConstants.NODESET);
    }

    public void serialize(Document document, OutputStream out) {
        try {
            OutputFormat format = new OutputFormat(document);
            XMLSerializer output = new XMLSerializer(out, format);
            format.setIndenting(true);

            output.serialize(document);
        }
        catch (IOException e) {
            System.err.println(e);
        }
    }


}
